vue项目多环境配置方案

您所在的位置:网站首页 vue 后端接口 vue项目多环境配置方案

vue项目多环境配置方案

2023-03-26 01:43| 来源: 网络整理| 查看: 265

简介

项目:使用vue-cli-service作为启动devServer以及打包构建工具的vue项目。

需求场景:

开发:启动各环境的devServer,联调时可以在本地调用不同环境的后端接口 构建:打包各环境的代码,不同环境对应的后端服务器地址不同,部署后各环境的前端项目会调用对应环境(开发、测试、uat、生产)的后端接口

开发过程中经常会出现以上使用场景,基于该背景,以下是从vue-cli-service源码出发梳理一套vue项目多环境配置方案过程中的一些记录(查看具体方案的可跳到最后的"方案实现"一节)

Mode

vue项目的多环境配置中需要重点关注的是vue-cli-service中的Mode(环境模式)的概念,vue-cli-service中有三种模式:

development:vue-cli-service serve执行时的默认模式。

test:vue-cli-service test:unit执行时的默认模式,安装@vue/test-utils插件后会注入该模式。

production:vue-cli-service build执行时的默认模式。 在执行命令时没有通过--mode命令行标识去指定模式的情况下,vue-cli-service会根据执行命令时不同的参数来推断一个默认的模式。

Mode与Environment

Mode的几个枚举值很容易与环境(Environment)的概念混淆,Mode有development/test/production几种,Environment可以有很多种,常见的有开发(development)/测试(test)/uat/生产(production)。

Mode

不同Mode下vue-cli-service会执行不同的操作,如development模式可以理解为当前执行vue-cli-service命令是在开发场景中执行的,vue-cli-service会自动生成一份针对开发优化的webpack配置并启用devServer,使得可以在本地打开项目以供调试。 development和production模式下各自生成的webpack配置会不一样,production模式会针对打包的代码进行优化,如production模式会添加代码压缩、分包、文件名添加hash值等配置。

Environment

不同环境指的是打包出来的代码的运行环境,使用场景有:在不同环境中的代码调用的后端接口地址可能不同,如在开发服务器上的前端项目调用后端接口域名为https://dev.xxx.com/apis,测试服务器上部署的前端项目调用的后端接口域名为https://test.xxx.com/apis

若概念混淆,当执行vue-cli-service build时Mode设置为了development而不是production,那么部署后的代码可能会出现没有代码压缩(体积很大)、文件名后没有hash值(每次更新都需要用户清空缓存刷新才能生效)等问题。

执行vue-cli-service xxx后发生了什么

该节只介绍mode相关的内容,其他部分不过多赘述。

PS:本节内容搭配@vue/cli-service的源码进行食用口感最佳。

1、入口

vue-cli-service命令的入口文件为@/vue/cli-service/bin/vue-cli-service.js,即执行vue-cli-service命令背后逻辑是执行该文件,查看可知通过vue-cli-service xxx传入的参数经过minimist处理后会传入service.run方法中。

const rawArgv = process.argv.slice(2) const args = require('minimist')(rawArgv, { boolean: [ // build 'modern', 'report', 'report-json', 'inline-vue', 'watch', // serve 'open', 'copy', 'https', // inspect 'verbose' ] }) // vue-cli-service xxx命令后第一个参数,正常情况下为serve/build/test:unit const command = args._[0] service.run(command, args, rawArgv).catch(err => { error(err) process.exit(1) }) 复制代码 2、确定mode # @vue/cli-service/lib/Service.js class Service{ // modes为初始化过程中通过resolvePlugins方法加载@vue/cli-service/lib/commands/下的文件提供,具体可细看constructor中的内容,此处直接将加载后的结果展示以供理解mode的确定逻辑 modes={ "serve": "development", "test:unit": "test", "build": "production" } // name=serve|build|test:unit run(name, args = {}, rawArgv = []) { const mode = args.mode || (name === 'build' && args.watch ? 'development' : this.modes[name]) } } 复制代码

如上可知mode的推断逻辑(按优先级排列):

vue-cli-service serve --mode development:通过--mode指定mode优先级最高 vue-cli-service build --watch:执行该命令mode默认指定为development 其余场景根据modes[name]自动匹配 3、初始化环境变量

调用service.loadEnv方法并通过dotenv库依次读取并加载项目根目录下的.env.[mode].local和.env.[mode]文件,将内部的变量一一分配到process.env中。

4、确定NODE_ENV # @vue/cli-service/lib/Service.js Service.loadEnv() const defaultNodeEnv = (mode === 'production' || mode === 'test') ? mode : 'development' if (process.env.NODE_ENV == null) { process.env.NODE_ENV = defaultNodeEnv } if (process.env.BABEL_ENV == null) { process.env.BABEL_ENV = defaultNodeEnv } 复制代码

以上为定义process.env.NODE_ENV以及process.env.BABEL_ENV的逻辑。此部分用到的mode为“确定mode”一节中所得到的值。

PS(PS又来了):由上可知mode会影响process.env.NODE_ENV以及process.env.BABEL_ENV值的确定。后续vue-cli-service生成webpack配置、使用babel的过程中会用到这两个变量,这就解释了为什么mode的正确使用这么重要。如只有当process.env.NODE_ENV=production时打包后的代码才会有代码压缩、文件名添加hash等效果。 除非目的明确,否则不要在env文件中指定NODE_ENV,否则在如“初始化环境变量”一节中所示,提前通过加载env文件指定了process.env.NODE_ENV的话会影响默认NODE_ENV值的定义。

webpack config对比

通过vue-cli-service inspect --mode xxx可以得到development和production模式下生成的webpack配置信息,部分区别如下:

Pasted image 20211219185705.png

Pasted image 20211219185833.png

Pasted image 20211219185923.png

方案实现

新目录结构如下:

Pasted image 20211219190144.png

环境相关变量文件从根目录移动至cli/environments/文件夹下,vue-cli-service原有逻辑会根据mode读取根目录下env文件。 cli/environments/下的env文件存放Environment相关变量,如:

# cli/environments/.env.dev # 用于识别当前环境 VUE_APP_ENV=dev # 接口服务器地址 API_HOST=https://dev.xxx.com/apis 复制代码

以下是cli/index.js的内容,根据@vue/cli-service/bin/vue-cli-service.js改造,添加了加载指定位置env文件的逻辑

# cli/index.js const { semver, error } = require("@vue/cli-shared-utils"); const requiredVersion = require("@vue/cli-service/package.json").engines.node; const dotenv = require("dotenv"); const dotenvExpand = require("dotenv-expand"); const path = require("path"); if ( !semver.satisfies(process.version, requiredVersion, { includePrerelease: true, }) ) { error( `You are using Node ${process.version}, but vue-cli-service ` + `requires Node ${requiredVersion}.\nPlease upgrade your Node version.` ); process.exit(1); } const Service = require("@vue/cli-service/lib/Service"); const service = new Service(process.env.VUE_CLI_CONTEXT || process.cwd()); const rawArgv = process.argv.slice(2); const args = require("minimist")(rawArgv, { boolean: [ // build "modern", "report", "report-json", "inline-vue", "watch", // serve "open", "copy", "https", // inspect "verbose", ], }); const command = args._[0]; const env = args.env || "dev"; loadEnv(env); service.run(command, args, rawArgv).catch((err) => { error(err); process.exit(1); }); /** * @description: 加载env文件 * @param {String} env 环境(dev|test|uat|prod) */ function loadEnv(env) { try { const envOptions = ["dev", "test", "uat", "prod"]; if (!envOptions.includes(env)) { throw new Error( `env: ${env} is invalid, options: ${JSON.stringify(envOptions)}` ); } const envPath = path.resolve(process.cwd(), `cli/environments/.env.${env}`); const localPath = `${envPath}.local`; // 加载.env.local文件 const localEnvConfig = dotenv.config({ path: localPath, debug: process.env.DEBUG, }); dotenvExpand(localEnvConfig); // 加载env文件 const envConfig = dotenv.config({ path: envPath, debug: process.env.DEBUG }); dotenvExpand(envConfig); } catch (err) { // 忽略文件不存在错误 if (err.toString().indexOf("ENOENT") < 0) { error(err); process.exit(1) } } } 复制代码

最后修改package.json文件中的scripts:

# package.json { "scripts": { "serve:dev": "node ./cli serve --env dev", "serve:test": "node ./cli serve --env test", "serve:uat": "node ./cli serve --env uat", "serve:prod": "node ./cli serve --env prod", "build:dev": "node ./cli build --env dev", "build:test": "node ./cli build --env test", "build:uat": "node ./cli build --env uat", "build:prod": "node ./cli build --env prod" } } 复制代码 总结

总结一下这套方案,主要做了以下工作:

区分环境变量文件和模式变量文件,模式变量文件放在项目根目录,vue-cli-service会自动加载。环境变量文件移动到/cli/environments/目录下,由/cli/index.js中添加的逻辑单独加载处理。 新增/cli/index.js文件,vue-cli-service的原有逻辑迁移至该文件,并添加加载/cli/environments/目录下的环境变量文件的逻辑,手动处理需要的环境变量文件。

如有其他想法,欢迎指教



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3